#ifndef POST_PROCESSOR_H_
#define POST_PROCESSOR_H_
#include <mutex>
#include <string>

#include "common/perception_defs.h"
#include "postprocess/matcher.h"
#include "postprocess/object_track.h"
#include "postprocess/post_process_variable.h"

struct PostProcessorSettings {};

class PostProcessor final {
 public:
  PostProcessor(PostProcessor const &) = delete;
  PostProcessor &operator=(PostProcessor const &) = delete;
  PostProcessor(PostProcessor &&) = delete;
  PostProcessor &operator=(PostProcessor &&) = delete;
  ~PostProcessor();

  ///@brief Get instance
  ///@return PostProcessor
  static PostProcessor &GetInstance() noexcept;

  ///@brief init
  ///@param settings PostProcessorSettings
  /// @param err error message
  /// @return if no error return true, otherwise false
  bool Init(PostProcessorSettings const &settings, std::string &err) noexcept;

  /// @brief do post process
  /// @param perceptionResult perception result
  /// @param err error message
  /// @return if no error return true, otherwise false
  bool Process(PerceptionResult &perceptionResult, std::string &err) noexcept;

  ///@brief GetSettings
  ///@return PostProcessorSettings
  PostProcessorSettings GetSettings() const noexcept { return settings_; }

 protected:
  /// @brief constuctor
  PostProcessor() noexcept;

 private:
  // @brief transform tracker from bbox
  // @params[IN]
  // @params[IN]
  // @params[OUT]
  void transform_tracker_from_bbox(TrackedObjectPtr *tracker,
                                   const BoundingBox &bbox) noexcept;

  // @brief predict trackder
  // @params[IN]
  // @params[IN]
  // @params[OUT]
  void predict(const PerceptionResult &percetion, double time_diff) noexcept;

  /// @brief create bbox index form : 0 ~ n-1
  /// @param
  /// @return
  void range(const FixedArray<BoundingBox, kMaxBoundingBoxNum> &dl_boxes,
             std::vector<std::int32_t> &objects_index);

  /// @brief constrcut tracked_object from lidar_object
  /// @param
  /// @return
  void construct_tracked_objects(
      const FixedArray<BoundingBox, kMaxBoundingBoxNum> &dl_boxes,
      std::vector<TrackedObjectPtr> *tracked_objects);

  /// @brief collect tracked results
  /// @param[OUT] lidar_objects: tracked objects with tracking information
  /// @return nothing
  void collect_tracked_results(PerceptionResult &perceptionResult);

  /// @brief compute predicted states of maintained tracks
  /// @param[OUT] tracks_predict: predicted states of maintained tracks
  /// @param[IN] time_diff: time interval for predicting
  /// @return nothing
  void compute_tracks_predict(std::vector<Eigen::VectorXf> &tracks_predict,
                              double time_diff);

  /// @brief create new tracker object
  /// @param void
  /// @return void
  void create_new_tracks(
      std::vector<TrackedObjectPtr> const &new_objects,
      std::vector<std::int32_t> const &unassigned_objects_index);

  /// @brief update assigned tracks
  /// @params[IN] tracks_predict: predicted states of maintained tracks
  /// @params[IN] new_objects: recently detected objects
  /// @params[IN] assignments: assignment pair of <track, object>
  /// @params[IN] time_diff: time interval for updating
  /// @return nothing
  void update_assigned_tracks(std::vector<TrackedObjectPtr> *new_objects,
                              std::vector<TrackObjectPair> const &assignments,
                              double time_diff);

  /// @brief update tracks without matched objects
  /// @param[IN] tracks_predict: predicted state of maintained tracks
  /// @param[IN] unassigned_tracks: index of unassigned tracks
  /// @param[IN]
  /// @param[IN]
  /// @param[IN] time_diff: time interval for updating
  /// @return nothing
  void update_unassigned_tracks(
      std::vector<Eigen::VectorXf> const &tracks_predict,
      std::vector<std::int32_t> const &unassigned_tracks, double time_diff);

  /// @brief delete lost tracks
  /// @return nothing
  void delete_lost_tracks();

  ///@brief mutex , multi thread safe
  std::mutex mutex_;
  ///@brief init flag
  bool is_init_;
  /// @brief settings
  PostProcessorSettings settings_;
  /// @brief previous timestamp
  uint64_t pre_timestamp_;
  /// @brief initialize track
  bool track_initialization_flag_;
  /// @brief track prediction
  FixedArray<TrackedObject, kMaxTrackerObjectNum> tracker_prediction_objects_;
  /// @brief tracker Set
  ObjectTrackSet object_tracks_;
  /// @brief hungarian matcher
  std::unique_ptr<Mathcer> matcher_;
};

#endif  // !POST_PROCESSOR_H_
